Skip to content

feat: link a plugin's documentation from the plugin editor - #3462

Merged
LiteSun merged 3 commits into
apache:masterfrom
lxbme:feat/plugin-doc-link
Aug 4, 2026
Merged

feat: link a plugin's documentation from the plugin editor#3462
LiteSun merged 3 commits into
apache:masterfrom
lxbme:feat/plugin-doc-link

Conversation

@lxbme

@lxbme lxbme commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Please answer these questions before submitting a pull request, or your PR will get closed.

Why submit this pull request?

  • Bugfix
  • New feature provided
  • Improve performance
  • Backport patches

What changes will this PR take into?

Choosing a plugin meant picking from a grid of bare names — ai-aws-content-moderation, graphql-proxy-cache, workflow, api-breaker — with no description and nothing to click. Once a plugin was open, the drawer showed its name and the editor was handed that plugin's JSON schema, so there was validation and completion, but nothing anywhere said what the plugin was for, and nothing linked out. A user who did not already know the plugin had to leave the dashboard and search for it.

Each plugin now carries a link to its documentation: an icon on the plugin card, and an icon with a label in the editor drawer's title row.

Why a link rather than a description

The gateway cannot supply the text. Of the plugins returned by GET /apisix/admin/plugins?all=true on APISIX 3.17.0, none carries a schema.description. Bundling a name-to-description table in the dashboard would mean 107 entries times five locales, maintained against a plugin list that changes every gateway release — and a stale description is worse than none. The docs site is the only available source, and its URL is derivable from the plugin name.

The URL is derived, and the exceptions are enumerated

src/utils/pluginDocs.ts maps a plugin name and the UI language to a URL or to null. Coverage was measured against the live docs site across every plugin name the gateway reports in both subsystems (106 http plus mqtt-proxy from stream):

Result Count
Resolves on the released docs 100 / 107
Documented under a different name 2 (serverless-pre-function, serverless-post-function share /plugins/serverless/)
No page at all 2 (example-plugin is the gateway's sample; ai has no page of its own)
Newer than the current docs release 3 (ai-cache, ai-lakera-guard, mcp-bridge — they resolve only under /next/)

The two serverless-* names get a slug override. The other five render no link at all: a dashboard that is the first thing many people see of APISIX should not hand out broken links, and a missing affordance is a smaller failure than one that lies.

The three unreleased plugins deliberately do not link /next/. That would resolve today, but it points users at documentation for a version they are not running, and those entries would expire silently once the released docs catch up. Their absence is self-correcting; a stale /next/ pointer is not.

Following the UI language

The link resolves to /zh/docs/... when the dashboard is in Chinese and to the English docs otherwise. Coverage was measured separately for Chinese and is identical — 100 of 107, and the same seven names fail in both locales, character for character — so following the language introduces no failure mode that English does not already have. The docs site publishes English and Chinese only; /es/, /de/ and /tr/ are 404, so those three fall back to English rather than producing a URL that does not exist.

The comparison is exact (language === 'zh'), not a prefix match: src/config/i18n.ts derives supportedLngs from the resource keys, so the only possible values are the five bare codes with no regional variants. This matches how antdConfigProvider already indexes its locale table.

Why the control is an anchor

PluginDocsLink renders a Mantine Anchor, i.e. a real <a href target="_blank" rel="noopener noreferrer">, following the pattern Header/index.tsx already uses for the docs button. The repo's RouteLinkBtn is a Mantine Button carrying href; middle-click, ⌘-click and "copy link address" are exactly what someone reaching for documentation does, and none of them work on a <button>. RouteLinkBtn's own call sites are deliberately untouched.

The e2e assertions use getByRole('link', …), which fails if the control ever regresses to a button.

Two call sites, six surfaces

PluginCard and PluginEditorDrawer are both shared: the card renders in the select-plugins picker, in the list of already-selected plugins, and on the Plugin Metadata page; the drawer is used by the plugins form field and by Plugin Metadata. Two edits therefore cover every place a plugin is chosen or configured.

The link is unconditional on drawer mode — it shows in add, edit and view alike. What a plugin does is the same question whether you are choosing it, configuring it, or reading someone else's configuration, and view is what a read-only operator sees.

On the card the link is icon-only, with an aria-label naming the plugin, so a screen reader can tell which of a hundred identical icons it has landed on. In the drawer the label is visible and no aria-label is set: overriding a visible label with text that does not contain it would break WCAG 2.5.3 Label in Name. The drawer's label reuses the existing top-level docs key rather than introducing a second key meaning the same thing.

One new string, form.plugins.docsFor, in all five locales. It is interpolated with escapeValue: false, prophylactically — #3459 shipped an accessible name reading R&amp;D before that was caught.

Related issues

Closes #3461

Checklist:

  • Did you explain what problem does this PR solve? Or what new features have been added?
  • Have you added corresponding test cases?
  • Have you modified the corresponding document?
  • Is this PR backward compatible? If it is not backward compatible, please discuss on the mailing list first

Tests:

  • src/utils/pluginDocs.test.ts — 13 cases: a plain name; both serverless-* collapsing to serverless; each of the five undocumented names returning null; the /zh prefix; es/de/tr falling back to English rather than emitting a /es/ URL that does not exist; and the zh prefix combined with a slug override, which is the interaction the individual cases miss.

  • e2e/tests/regression/plugin.docs-link.spec.ts — five tests: the drawer link carries the exact expected href and target="_blank"; a plugin with no documentation page (example-plugin) exposes no link, which is the assertion that fails if the exception table is dropped; the same link's href gains /zh/ after switching the UI to Chinese; a picker card exposes a link whose accessible name is key-auth documentation, which is the only coverage of the component's icon-only branch; and an undocumented plugin's card exposes no link.

The card sits inside a Combobox.Option, so whether a nested anchor's click survives Mantine's option handling was checked by hand in a real browser rather than inferred: clicking the icon opens a new tab on the plugin's documentation page and leaves the picker open and unchanged. No stopPropagation was needed, so none was added.

Verified: pnpm test (122 passed), pnpm lint, pnpm exec tsc -b, and both a --mode test and a production pnpm build, all clean. The new spec passes 5/5 in isolation.

A full Playwright run on this machine is not currently a usable signal, and the reason is worth stating rather than hiding behind a pass rate. The suite's own bulk-1000 spec drives etcd into multi-second stalls — its logs show 51-second raft reads on the health-probe key — after which unrelated specs fail with has no healthy etcd endpoint available. Separately, this machine cannot run the Monaco-backed specs headless at all. Both were confirmed to be independent of this change by checking out master (89fc2ed1), rebuilding, and re-running the failures: every one of the eight specs sampled that way fails identically on master, and the specs that recovered once etcd was healthy — including this PR's own — recovered on both trees.

@LiteSun
LiteSun merged commit 045e314 into apache:master Aug 4, 2026
6 checks passed
@lxbme
lxbme deleted the feat/plugin-doc-link branch August 4, 2026 06:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: link a plugin's documentation from the plugin editor

3 participants